using System;
using System.Linq;
using System.Text;
using Generator.Model;

namespace Generator.Renderer.Public;

internal static class UntypedRecord
{
    public static string Render(GirModel.Record record)
    {
        var name = Model.UntypedRecord.GetPublicClassName(record);
        var internalHandleName = Model.UntypedRecord.GetFullyQuallifiedOwnedHandle(record);

        return $@"
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

#nullable enable

namespace {Namespace.GetPublicName(record.Namespace)};

// AUTOGENERATED FILE - DO NOT MODIFY

{PlatformSupportAttribute.Render(record as GirModel.PlatformDependent)}
public sealed partial class {name}
{{
    public {internalHandleName} Handle {{ get; }}

    public {name}({internalHandleName} handle)
    {{
        Handle = handle;
    }}

    public {name}() : this({Model.UntypedRecord.GetFullyQuallifiedOwnedHandle(record)}.Create())
    {{
    }}

    {record.Constructors
        .Select(ConstructorRenderer.Render)
        .Join(Environment.NewLine)}

    {record.Fields
        .Select(f => RenderField(record, f))
        .Join(Environment.NewLine)}

    {record.Functions
        .Select(FunctionRenderer.Render)
        .Join(Environment.NewLine)}

    {record.Methods
        .Where(Method.IsEnabled)
        .Select(MethodRenderer.Render)
        .Join(Environment.NewLine)}

    public bool Equals({name}? other)
    {{
        if (ReferenceEquals(null, other))
            return false;

        if (ReferenceEquals(this, other))
            return true;

        return Handle.Equals(other.Handle);
    }}

    public override bool Equals(object? obj)
    {{
        return ReferenceEquals(this, obj) || obj is {name} other && Equals(other);
    }}

    public override int GetHashCode()
    {{
        return Handle.GetHashCode();
    }}
}}";
    }

    private static string RenderField(GirModel.Record record, GirModel.Field field)
    {
        try
        {
            var renderableField = Fields.GetRenderableField(field);

            if (field is { IsReadable: false, IsWritable: false } || field.IsPrivate)
                return string.Empty;

            var result = new StringBuilder();

            result.AppendLine($"public {renderableField.NullableTypeName} {renderableField.Name} {{");

            if (field.IsReadable)
                result.AppendLine($"get => {renderableField.GetExpression(record, field)};");

            if (field.IsWritable)
                result.AppendLine($"set => {renderableField.SetExpression(record, field)};");

            result.AppendLine("}");

            return result.ToString();
        }
        catch (Exception ex)
        {
            Log.Warning($"Did not render typed record {record.Name} field {field.Name}: {ex.Message}");
            return string.Empty;
        }
    }
}
